
Ponizszy tekst ukazal sie w formie artykulu na lamach polskiej edycji
Magazynu Amiga.  Postanowilem jednak dolaczyc go w niezmienionej
formie takze jako tutorial do najnowszej edycji FlexCata. Przyklady
do tekstu zalaczone sa w postaci zrodel w jezyku C i znajduja sie
w katalogu "Sources/C".

Milej lektury ;-)


Ponizszy tekst oraz zwiazane z nim przykladowe zrodla nie moga byc
przedrukowywane ani publikowane w zadnej formie, papierowej badz
elektronicznej bez zgody autora tekstu.

©1997-1999 Marcin Orlowski <carlos@amiga.com.pl>


---------------------------------------


                       "Do you arbeit mit printer?"
                          czyli Amiga poliglotkâ


Mimo, ûe system operacyjny Amigi od dobrych paru lat wspomaga tworzenie
programów wielkojëzykowych (tzw.  zlokalizowanych) to jednak duûy procent
dostëpnych programów z tych dobrodziejstw, nie wiedzieê czemu, nie
korzysta...

1.  Sîowem wstëpu

Jak sië w dalszej czëôci przekonasz, decyzja aby napisaê wîasny program
wielojëzykowy, nie pociga za sobâ ûadnych wiëkszych problemów, za to
przynosi dodatkowe korzyôci.  Wbrew pozorom wiëkszoôê Polaków nie zna
jëzyka angielskiego w stopniu wystarczajâcym do bezbolesnej pracy z
programami nim tylko sië posîugujâcymi, podstawowym zaô bîëdem wiëkszoôci
programistów jest zaîoûenie, ûe kaûdy z uûytkowników dysponuje wiedzâ co
najmniej takâ jak oni sami.  W rzeczywistoôci nawet tak proste zwroty jak
"Editing Time", "Saved By", "End of text reached", czy "Talk As You Type"
przysparzajâ wielu problemów (z ciekawszych, niestety autentycznych,
tîumaczeï:  "Edycja czasu", "Zapisz obok", "Wyciâgam zakoïczenia z tekstu"
oraz "Wymowa wedîug parametrów").  Z resztâ szczególne traktowanie jëzyka
angielskiego teû nie jest reguîâ - ilu z was uruchamiajâc nawet ciekawie
zapowiadajâcy sië program, czym prëdzej wybierze "Beenden" widzâê jego
niemiecko-jëzyczny interfejs uûytkownika?

2.  Reguîy...

...sâ doôê proste.  Wybierasz jëzyk, który bëdzie podstawowym dla Twojego
programu (w wiëkszoôci przypadków jest to angielski).  Nastëpnie zabierasz
sie do pisania wîaôciwej treôci programu, z tâ drobnâ róûnicâ, ûe wszystkie
komunikaty programu, które normalnie umieôciîbyô w jego treôci, zastëpujesz
wywoîaniem odpowiedniej funkcji, zaô same teksty umieszczasz w dodatkowym
pliku, z którego bëdziesz je w razie potrzeby podczas pracy pobieraî.

3.  Katalogi

Powyûszy mechanizm pozwala na w miarë îatwâ zmianë wersji jëzykowej poprzez
zastâpienie pliku jego tîumaczeniem, i mogîoby sië wydawaê, ûe najlepszym
rozwiâzaniem problemu przechowywania tekstów jest uûycie zwykîego pliku
ASCII.  Niestety, najprostsze rozwiâzania nie zawsze sâ nalepsze.  Na
pierwsze problemy natkniemy sië juû przy modyfikacji zawartoôci takiego
zbioru.  Wystarczy niefrasobliwie usunâê jakikolwiek tekst ze ôrodka, bâdú
zmieniê ich kolejnoôê aby przekonaê sië jak bogatym zasobem przekleïstw
dysponujâ nasi tîumacze.  Przy tym nie mamy ûadnego wpîywu na dîugoôê
poszczególnych tekstów w pliku, gdyû przy prostocie edycji plików ASCII
mogâ sië one w niekontrolowany sposób zmieniaê.  Po trzecie nie mamy ûadnej
kontroli nad aktualnoôciâ wersji takiego pliku.  Po czwarte zaô, wystarczy
uruchomiê program korzystajâcy z pliku o dîugoôci 100KB kilka razy
(pamiëtajmy, ûe od 1995 roku ûyjemy w ôwiecie 32-bitowej wielozadaniowoôci)
aby zauwaûyê drastyczny spadek iloôci dostëpnej wolnej pamiëci. Po piâte
zaô zmuszamy uûytkownika do manipulowania plikami, za kaûdym razem gdy
bëdzie chciaî zmieniê wersjë jëzykowâ.

Lekarstwo na wymienione powyûej bolâczki pojawiîo sië po raz pierwszy w
wersji 2.05 systemu operacyjnego i nazywa sië locale.library.  Biblioteka
tak wprowadza wîasny sposób obsîugi tekstów, przechowujâc je w pliku zwanym
katalogiem (od angielskiego "catalog file").  Wykorzystanie tej biblioteki
pozwoli nam na proste zaimplementowanie obsîugi róûnych jëzyków w naszym
programie.

Katalog powstaje jako wypadkowa dwóch plików, opisu katalogu (ang.
"catalog descriptor") oraz jego tîumaczenia (ang.  "catalog translation")
"potraktowanych" odpowiednim programem.  Tîumaczenie to jak nazwa wskazuje
inna wersja jëzykowa tekstów programu i w tym artykule nie bëdziemy sië nim
zajmowaê.  Dokîadniej przyjrzymy sië natomiast opisowi katalogu , bowiem
plik ten sîuûy takûe do utworzenia domyôlnej (wbudowanej) wersji jëzykowej,
którâ program nasz bëdzie sië posîugiwaî w przypadku braku jakiegokolwiek
katalogu.  Poniewaû wiëkszoôê z programów sîuûâcych do tworzenia katalogów
ma dodatkowo moûliwoôê generowania odpowiednich tekstów úródîowych do ich
obsîugi, wiëc pracë mamy znacznie uîatwionâ.  Z dostëpnych obecnie narzëdzi
dwa sâ godne uwagi.  Sâ to programy CatComp oraz FlexCat.  CatComp
znajdowaî sië pierwotnie jedynie w pakiecie dostëpnym dla zarejestrowanych
developerów Amigi, co znacznie zawëûaîo krâg jego uûytkowników (obecnie
znaleúê go moûna takûe na pîycie DeveloperCD), natomiast FlexCat jest
programem darmowym i powszechnie dostëpnym w sieci Internet (Aminet katalog
dev/misc, stona WWW:  http://www.wfmh.pl/amiga/).  Wprawdzie oba potrafiâ
generowaê wspomniany juû kod úródîowy, lecz jedynie FlexCat dziëki
wykorzystaniu tekstów wzorcowych (patrz dokumentacja programu), obsîuguje
jëzyki inne niû C (obecnie sâ to Assembler, AmigaE, C++, Modula oraz
Oberon, zaô w razie potrzeby moûna samemu dodaê obsîugë kaûdego nowego
jëzyka).

4. Skîadnia

Kaûdy tekst w pliku opisowym poprzedzony jest odpowiedniâ deklaracjâ w
formacie:

ETYKIETA (ID/MIN/MAX)

gdzie ETYKIETA to unikalny ciâg znaków identyfikujâcy dany tekst, np.
MSG_TEXT_2.  ID to, równieû unikalny, identyfikator tekstu, zaô MIN i MAX
to dopuszczalna minimalna i maksymalna dîugoôê tîumaczenia danego tekstu.
ID, MIN oraz MAX sâ wartoôciami opcjonalnymi i mogâ zostaê pominiëte.  W
przypadku nie podania jawnie wartoôci ID, zostanie ona wygenerowana
automatycznie, zaô niepodanie MIN czy MAX oznacza brak narzuconych
ograniczeï (patrz listing #1).  Najprostsza forma deklaracji zatem to:

ETYKIETA (//)

Bezpoôrednio pod etykietâ umieszczamy wîaôciwy tekst.  Jeôli zajmowaê ma on
wiëcej niû jednâ linië, kaûdâ z nich koïczymy znakiem "\" (podobnie jak w
przypadku #define w jëzyku C).  W przypadku wykorzystania z sekwencji
specjalnych jak np.  %s, %ld czy '\n', '\t' zarówno FlexCat jak i CatComp
sprawdzâ ich poprawnoôê oraz ostrzegâ w przypadku uûycia niestandardowych
sekwencji.  Dopuszczalne sâ takûe linie komentarza, które rozpoczyna znak
ôrednika umieszczony w pierwszej kolumnie.

5.  Funkcje biblioteczne

Uûywanie katalogów nie odbiega wcale do pracy z bibliotekami systemowymi.
przed ich uûyciem naleûy katalog otworzyê (otwierajâc uprzednio bibliotekë
locale.library):

struct Catalog *OpenCatalog(Locale, Nazwa, Tag, ...);

gdzie Locale to wskaúnik na strukturë Locale, dla których chcemy dany
katalog otworzyê.  Zazwyczaj podaje sië wartoôê NULL, co powoduje uûycie
struktury aktualnie uûywanej przez system (a ustawionej przez uûytkownika
za pomocâ panelu preferencji Locale).  Nazwa okreôla fizycznâ nazwë
uûywanego przez program katalogu.  Naleûy podaê jedynie nazwë pliku (np.
"HelloWorld.catalog"), do której dodana zostanie ôcieûka np.
"PROGDIR:Catalogs/" + "polski/" (nazwa kolejnego, ustawionego przez
uûytkownika systemu, preferowanego jëzyka pobrana ze struktury Locale).
Dodatkowe tagi umoûliwiajâ wymuszenie otwarcia katalogu o konkretnej wersji
(OC_Version - w odróûnieniu od OpenLibrary(), numer wersji katalogu musi
byê identyczny z ûâdanym, aby wykonanie funkcji sië powiodîo), czy
okreôlenie jëzyka (OC_BuiltInLanguage) jaki jest w program wbudowany (w
naszym przykîadzie jëzyk wbudowany okreôlimy uûywajâc atrybutu #language,
króry jest rozszerzeniem skîadni wprowadzonym przez FlexCata).  Podanie
NULL oznacza iû program nie posiada wbudowanych tekstów, jednakûe sytuacja
taka nie jest wskazana, choê moûe byê przydatne w przypadku programów
majâcych bardzo duûâ bazë komunikatów.  Po skoïczonej pracy katalog naleûy
zamknâê:

void CloseCatalog(struct Catalog *catalog);

W odróûnieniu od wiëkszoôci innych funkcji systemowych, NULL jako wynik
OpenCatalog() nie oznacza bîëdu, a jedynie fakt nieznalezienia pliku z
ûâdanâ wersjâ jëzykowâ.  NULL jest takûe poprawnym argumentem funkcji
CloseCatalog().

Poszczególne teksty pobieraê bëdziemy za pomocâ funkcji:

STRPTR GetCatalogStr(struct Catalog *catalog, LONG Numer, STRPTR TekstDomyôlny);

gdzie Numer to ID ûâdanego tekstu, zaô TekstDomyôlny to wskaúnik na tekst
który uûyty bëdzie gdy ciâg znakow o ûâdanym ID nie zostaî w katalogu
znaleziony bâdú wskaúnik catalog równy jest NULL (np.  z powodu braku
katalogu).  Jako rezultat otrzymamy wskaúnik na interesujâcy nas tekst.
Pozostaje on waûny do momentu zamkniëcia katalogu i pamiëtaê naleûy, ûe tak
jak biblioteki czy czcionki, katalog wczytywany jest do pamiëci tylko raz,
podczas pierwszego otwarcia, zatem ûe tekst na który wskaúnik otrzymaliômy
jest tylko do odczytu.

6.  Program przykîadowy

Do utworzenia niezbëdnych procedur obsîugi katalogów oraz wersji domyôlnej
posîuûymy sië w naszym przykîadzie FlexCatem.  Po zapisaniu na dysku opisu
katalogu (listing #1) oraz úródîa wîaôciwego (listing #2), uruchamiamy
FlexCata:

FlexCat HelloWorld.cd HelloWorld_str.c=AutoC_c.sd
FlexCat HelloWorld.cd HelloWorld_str.h=AutoC_h.sd

Dodatkowy tekst úródîowy utworzony na podstawie wzorca AutoC wykorzystuje
bardzo wygodny mechanizm autoinicjalizacji/autoterminacji, który w tym
przypadku zwalnia nas od rëcznego wywoîywania funkcji
otwierajâcych/zamykajâcych biblioteki oraz plik katalogu.  Jeôli uûywasz
kompilatora innego niû SAS/C, Dice badú gcc musisz dopisaê wywoîania
funkcji _STIOpenCatalog() i _STICloseCatalog(), bâdú uûyê innego wzorca.
Dolâczamy do naszego úródîa wygenerowany plik HelloWorld_str.h aby móc sië
do tekstów odwoîywaê za pomocâ etykiet, a nastëpnie caîoôê kompilujemy:

SC HelloWorld.c HelloWorld_str.c LINK PROGNAME=HelloWorld

i uruchamiamy.  Przykro mi jeôli spodziewaîeô sie w tym miejscu
fajerwerków.  Dopóki nie utworzymy dodatkowych katalogów do programu, to
wizualnie nie bëdzie sië róûniî od swoich "normalnych" braci.  Ale to
dopiero za miesiâc.

7.  Drobiazgi uîatwiajâce ûycie

Wszystkim którzy tîumaczyê bëdâ Twój program z pewnoôciâ pomoûesz, jeôli
uwzglëdnisz poniûszych parë "drobiazgów" (a z wîasnego doôwiadczenia wiem
jak irytujâcy moûe byê fakt ich braku):

* Etykietë  kaûdego  tekstu  rozpoczynaj  od  przedrostka  MSG_.  Znacznie
  uîatwia to póúniejszâ aktualizacjë istniejâcych katalogów, gdyû w oparciu
  o przedrostek moûna szybko stworzyê prostâ makrodefinicjë (np.  uûywajâc
  CEDa), która skopiuje istniejâce tîumaczenia z poprzedniej edycji.
  Oczywiôcie moûesz uûyê innego przedrostka (byle w identycznego dla wszystkich
  etykiet), jednak utarîo sië aby byî to ciâg "MSG_".

* Unikaj  enigmatycznych identyfikatorów.  Wprawdzie doskonale wiesz kiedy
  kaûdy z tekstów jest uûywany, ale wiekszoôê tîumaczy zdolnoôci
  jasnowidzenia nie posiada i raczej na pewno nie zgadnie gdzie dany tekst
  wykorzystujesz.  Tîumaczenie moûe wtedy byê zrobione na tzw.  "oko" i
  nie koniecznie musi wtedy pasowaê do kontekstu caîoôci.  Po prostu pisz
  MSG_MAIN_WINDOW_TITLE zamiast MSG_13254 (palce na pewno Cië nie
  rozbolâ).

* Nie uûywaj etykiet w jëzyku polskim (lub jak kto woli pisz po angielsku).
  W poîâczeniu z poprzednim "drobiazgiem" pozwoli to tîumaczowi zorientowaê
  sië w kontekscie uûycia danego tekstu i odpowiednio to przetîumaczyê, w
  przeciwnym razie uraczysz go odpowiednikiem MSG_BEHAGA_NEDTRYCKA_ENTER

* Nie bâdú skâpy.  Pamiëtaj ûe w zaleûnoôci od kontekstu tîumacz moûe
  chcieê zastosowaê rózne tîumaczenia tego samego wyrazu czy choêby go
  odmieniê (aby uniknâê îamaïców typu "OKNO/NOWY").  Pozwól mu wiëc na to i
  nie wykorzystuj jednego tekstu do wielu zastosowaï.

* Rzadko kiedy tîumacz ma tyle wolnego czasu (albo ochoty) aby przeîoûyê
  wszystkie teksty za jednym posiedzeniem.  Czësto teû chciaîby popracowaê
  chwilë z niedokoïczonym tîumaczeniem (co pozwala wykryê niedokîadnoôci
  czy choêby literówki).  I tu napotyka na pewien problem.  Poniewaû
  przekîad nie jest skoïczony, pewien procent pól w pliku #?.ct jest pustych.
  Katalog wygenerowany na jego podstawie bëdzie wiëc zawieraî jedynie
  identyfikatory danych tekstów. Pobranie takiego tekstu da nam w rezultacie
  wskaúnik na ciâg znaków o dîugoôci zero i jeôli go uûyjemy zaowocuje to
  nam pustymi gadûetami czy polami menu. Aby temu zaradziê zmodyfikujemy
  nieznacznie wzorzec funkcji GetString() zawarty w pliku AutoC_c.sd
  FlexCata, tak aby w przypadku napotkania pustego ciâgu zwracaî nam tekst
  wbudowany. Zmodyfikowana funkcja wyglâdaê moûe np. tak:

  STRPTR GetString(struct FC_Type *fcstr)
  {
  STRPTR defaultstr = fcstr->Str;
  LONG   strnum     = fcstr->ID;
  STRPTR str;

	str = (%b_Catalog ? GetCatalogStr(%b_Catalog, strnum, defaultstr) :
			defaultstr);
	
	return((strlen(str) > 0) ? str, defaultstr);
  }

  Najoptymalniej byîoby aby takie zachowanie byîo opcjonalne i domyôlnie
  wyîâczone. Jednakûe brak takiej moûliwoôci jest znacznie mniej dotkliwy
  niû puste gadûety.

* Funkcja OpenCatalog() szuka katalogu który chcesz otworzyê najpierw w
  "PROGDIR:Catalogs/" a dopiero potem w "LOCALE:Catalogs/", zatem jeôli
  nie masz naprawdë powaûnych powodów ku temu, nie instaluj katalogów
  od swojego programu w tym ostatnim.


8.  W nastëpnym odcinku...

Za miesiâc ostatnia czëôê z naszego mini cykly, w której, dla odmiany
przyjrzymy sië sië programom od oczyma tîumacza oraz nauczymy nasz
przykîadowy programik jëzyków obcych (czyli pokaûemy po co naprawdë to
wszystko nam byîo potrzebne).
